package com.jasonclawson.dropwizardry.jersey.errors;
import io.dropwizard.jersey.errors.ErrorMessage;
import java.util.concurrent.ThreadLocalRandom;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Provider
public abstract class LoggingDebugExceptionMapper<E extends Throwable> implements ExceptionMapper<E> {
private static final Logger LOGGER = LoggerFactory.getLogger(LoggingDebugExceptionMapper.class);
private final boolean debuggable;
public LoggingDebugExceptionMapper(boolean debuggable) {
this.debuggable = debuggable;
}
/**
* This will be injected as a proxy of the request scoped servlet request
*/
@Context HttpServletRequest request;
private boolean isDebuggable() {
String debugHeader = request.getHeader("X-Debug");
return this.debuggable && "true".equalsIgnoreCase(debugHeader);
}
@Override
public Response toResponse(E exception) {
if (exception instanceof WebApplicationException) {
return ((WebApplicationException) exception).getResponse();
}
boolean debuggable = isDebuggable();
final long id = ThreadLocalRandom.current().nextLong();
/*
* If we are a BaseWebApplicationException then we will use its data to build a
* response.
*/
if(exception instanceof BaseWebApplicationException) {
ResponseBuilder builder = ((BaseWebApplicationException) exception).createResponseBuilder();
if(!debuggable) {
return builder.entity(new ErrorMessage(exception.getMessage()))
.build();
} else {
builder.entity(new DebugErrorMessage(id, exception));
debugLogException(id, exception);
return builder.build();
}
}
//here we have an unexpected exception of some kind (its not a BaseWebApplicationException)
//we must sanitize the exception message we send back to the client
//unless its debuggable
logException(id, exception);
if(debuggable) {
return Response.serverError()
.entity(new DebugErrorMessage(id, exception))
.build();
} else {
return Response.serverError()
.entity(new ErrorMessage(formatErrorMessage(id)))
.build();
}
}
protected static String formatErrorMessage(long id) {
return String.format("There was an error processing your request. It has been logged (ID %016x).", id);
}
protected void logException(long id, E exception) {
LOGGER.error(formatLogMessage(id, exception), exception);
}
protected void debugLogException(long id, E exception) {
LOGGER.debug(formatLogMessage(id, exception), exception);
}
protected static String formatLogMessage(long id, Throwable exception) {
return String.format("Error handling a request: %016x", id);
}
}